package util;

/**
 * @author Egor Kulikov (kulikov@devexperts.com)
 */
class GeometryUtils {
	public static final double SQUARE_EPS = 1e-5;
	public static final double EPS = 1e-8;

	public static class Point {
		private final double x;
		private final double y;

		public Point(double x, double y) {
			this.x = x;
			this.y = y;
		}

		public double distance(Point other) {
			return Math.sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y));
		}

		@Override
		public String toString() {
			return "(" + x + " " + y + ")";
		}
	}

	public static class Line {
		private final double a, b, c;

		public Line(Point a, Point b) {
			this.a = a.y - b.y;
			this.b = b.x - a.x;
			c = -a.x * this.a - a.y * this.b;
		}

		public Point intersect(Line other) {
			if (Math.abs(a * other.b - b * other.a) < EPS)
				return null;
			double x = (b * other.c - c * other.b) / (a * other.b - b * other.a);
			double y = (a * other.c - c * other.a) / (b * other.a - a * other.b);
			return new Point(x, y);
		}
	}

	public static class Segment {
		private final Point a;
		private final Point b;

		public Segment(Point a, Point b) {
			this.a = a;
			this.b = b;
		}

		public double length() {
			return a.distance(b);
		}

		public Point middle() {
			return new Point((a.x + b.x) / 2, (a.y + b.y) / 2);
		}

		public Point intersect(Segment other) {
			Line first = new Line(a, b);
			Line second = new Line(other.a, other.b);
			Point point = first.intersect(second);
			if (point != null && inside(point) && other.inside(point))
				return point;
			return null;
		}

		private boolean inside(Point point) {
			return Math.abs(length() - point.distance(a) - point.distance(b)) < EPS;
		}

		@Override
		public String toString() {
			return "{" + a + " " + b + "}";
		}
	}

	public static class Triangle {
		private final Point[] vertices;
		private final Segment[] sides;

		public Triangle(double x1, double y1, double x2, double y2, double x3, double y3) {
			vertices = new Point[]{new Point(x1, y1), new Point(x2, y2), new Point(x3, y3)};
			sides = new Segment[]{new Segment(vertices[0], vertices[1]), new Segment(vertices[1], vertices[2]),
				new Segment(vertices[2], vertices[0])};
		}

		public boolean inside(Point point) {
			double square = square();
			double allegedSquare = 0;
			for (int i = 0; i < 3; i++)
				allegedSquare += square(point, vertices[(i + 1) % 3], vertices[(i + 2) % 3]);
			return Math.abs(square - allegedSquare) < SQUARE_EPS;
		}

		private double square() {
			return square(vertices[0], vertices[1], vertices[2]);
		}

		private double square(Point a, Point b, Point c) {
			return .5 * Math.abs((a.x - b.x) * (a.y + b.y) + (b.x - c.x) * (b.y + c.y) + (c.x - a.x) * (c.y + a.y));
		}
	}
}
